$MOD186
$EP
NAME TIMER
; Main program for uPD70208 microcomputer system
;
; Author: 	Dr Tay Teng Tiow
; Address:     	Department of Electrical Engineering 
;         	National University of Singapore
;		10, Kent Ridge Crescent
;		Singapore 0511.	
; Date:   	6th September 1991
;
; This file contains proprietory information and cannot be copied 
; or distributed without prior permission from the author.
; =========================================================================

public	serial_rec_action, timer2_action
extrn	print_char:far, print_2hex:far, iodefine:far
extrn   set_timer2:far

STACK_SEG	SEGMENT
		DB	256 DUP(?)
	TOS	LABEL	WORD
STACK_SEG	ENDS

DATA_SEG	SEGMENT
;ASCII 10: go to next line, ; \n
;ASCII 13: move to the start of the line \r
	TIMER0_MESS	DB	10,13,'TIMER2 INTERRUPT    '
	T_COUNT		DB	0FH
	T_COUNT_SET	DB	0FH
	REC_MESS	DB	10,13,'Period of timer0 =     '
	COUNTER 	DB  00H ; whole process counter (for debugging)

	SegPattern	db	0AFh, 028h, 09Dh, 0B9h, 03Ah, 0B3h, 0B7h, 029h, 0BFh, 0BBh
	SegNum		db	001h, 002h, 003h, 004h, 005h, 006h
	SegPosition	db	0BFh, 0DFh, 0EFh, 0F7h, 0FBh, 0FDh

	SegCounter  db 0

;T1_top: t1 top counter, T1: intial value of counter 
;reduce the timer at MISC.ASM 
;change to word if the value of top counter is more than 256
	T1_top 		db 	1	; 3ms => 333.3Hz, each display at 333/6 = 55.5Hz
	T1 			db 	00h ;
	T1_prev		db  00h
	
	T2_top		db 	33 	; 33*3=99ms => ~10Hz
	T2 			db 	01H
	T2_prev 	db  00h

	T3_top		db 	66 ; 66*3=198ms => ~5Hz
	T3 			db 	05H
	T3_prev 	db  00h

	KEY_PREV	DB  0FFH

;initialize of the queue 
	KEY_QUEUE_LEN	EQU	128 ; length of the queue
	KEY_QUEUE_TRANS	DB	KEY_QUEUE_LEN DUP(?) ; reserve a space for the queue
	KEY_QUEUE_HEAD	DW	0H ; head of queue
	KEY_QUEUE_TAIL	DW	0H ; tail of queue

	led_counter	db 0
DATA_SEG	ENDS

; DATA segment for debug messages
DEBUG_SEG segment
	DEBUG_AL	DB  10,13,'current AL =    '
	DEBUG_T1 	DB 	10,13,'T1-Period =    '
	DEBUG_T2 	DB 	10,13,'T2-Period =    '
	DEBUG_KEYPAD_INPUT DB 10,13,'KEYPAD INPUT    '
	DEBUG_DEQUEUE DB  10,13,'Dequeue    '
	DEBUG_PRIORITY DB 10,13,'collision..... T1 has priority'
DEBUG_SEG ENDS


CODE_SEG	SEGMENT
PUBLIC		START
	PPI_8255_PORTA EQU 080H ;port a
	PPI_8255_PORTB EQU 081H ;port b
	PPI_8255_PORTC EQU 082H ;port c
	PPI_8255_CWR EQU 083H ;command word register
	PCS0 EQU 000H
	PCS1 EQU 080H
	PCS2 EQU 100H
	PCS3 EQU 180H

;to store the address of the queue space
	KEY_QUEUE_BASE EQU OFFSET KEY_QUEUE_TRANS

;$include(7seg.inc)

ASSUME	CS:CODE_SEG, SS:STACK_SEG, DS:DATA_SEG
START:

;initialize stack area
	MOV	AX,STACK_SEG		
	MOV	SS,AX
	MOV	SP,TOS

;initialize data segment register
	MOV	BX,DATA_SEG		
	MOV	DS,BX

; Initialize the on-chip pheripherals
	CALL	FAR PTR	IODEFINE
; ^^^^^^^^^^^^^^^^^  Start of User Main Routine  ^^^^^^^^^^^^^^^^^^
	call set_timer2
	STI

; Set CWR - All output
	MOV DX,PPI_8255_CWR
	MOV	AL,82H
	OUT DX,AL

MAIN_LOOP:
;	mov AL,DS:T1 
;	mov BL,DS:T2
;	or AL,BL
;	cmp AL,00H
;	je PRIORITY_CHECK

; if(), if(), if() - no priority checking
; all the process (in total of three processes) will run 
	mov AL,DS:T1
	cmp AL,00h
	je T1_ACTION
Proc_1_end:
	
	mov AL,DS:T2
	cmp AL,00h
	je T2_ACTION
Proc_2_end:

	mov AL,DS:T3
	cmp AL,00h
	je T3_ACTION
Proc_3_end:

	jmp MAIN_LOOP

;PRIORITY_CHECK:
;	CALL FAR PTR PRINT_PRIORITY_CHECK_MSG_DEBUG
;	JMP T1_ACTION

T1_ACTION:
;reset counter back to the top counter value, and it will decrement the value itself
	mov AL,DS:T1_top
	mov DS:T1,AL
;print current time	
	mov AL,DS:COUNTER
	mov BL,DS:T1_prev
	sub AL,BL

	cmp AL,DS:T1_top
	je START_T1
	call far ptr PRINT_T1_PERIOD_DEBUG
START_T1:
;----------------- loop T1 ----------------
	call far ptr display7segment
;----------------- loop T1 ----------------

	mov AL,DS:COUNTER
	mov DS:T1_prev,AL
	JMP Proc_1_end

T2_ACTION:
;reset counter
	mov AL,DS:T2_top
	mov DS:T2,AL
;print current time	
;	mov AL,DS:COUNTER
;	mov BL,DS:T2_prev
;	sub AL,BL
;	call far ptr PRINT_T2_PERIOD_DEBUG

;{------------------- LOOP T2 -----------------------
	call far ptr READ_KEYPAD
;	call far ptr PRINT_AL_DEBUG
	mov BL,AL
	cmp AL,DS:KEY_PREV
	je END_READ_KEYPAD
	
	mov DS:KEY_PREV,BL
	mov AL,BL
	cmp BL,0FFh
	je END_READ_KEYPAD

	call far ptr KEY_ENQUEUE
	call far ptr PRINT_KEYPAD_INPUT_DEBUG

END_READ_KEYPAD:

;---------------------------------------------------}

	mov AL,DS:COUNTER
	mov DS:T2_prev,AL
	JMP Proc_2_end

T3_ACTION:
;reset counter
	mov AL,DS:T3_top
	mov DS:T3,AL
;print current time	
;	mov AL,DS:COUNTER
;	mov BL,DS:T3_prev
;	sub AL,BL
;	call far ptr PRINT_T3_PERIOD_DEBUG
;----------------- loop T3 ---------------
;cashier program (dequeue the processes)-
	MOV BX,DS:KEY_QUEUE_TAIL
	CMP BX,DS:KEY_QUEUE_HEAD ; return if the tail is the same as head 
	JE END_KEY_DEQUEUE ; if Queue is empty: skip

	MOV BX,DS:KEY_QUEUE_HEAD
	MOV AL,DS:KEY_QUEUE_BASE[BX]

; need to change depends on our process 
	call far ptr PRINT_DEQUEUE_MSG_DEBUG
	inc DS:KEY_QUEUE_HEAD	
	call far ptr fake_process_delay_debug

	cmp DS:KEY_QUEUE_HEAD,KEY_QUEUE_LEN
	JL END_KEY_DEQUEUE		;if head >= len, wrap around
	MOV DS:KEY_QUEUE_HEAD,0

END_KEY_DEQUEUE:
;----------------- loop T3 ----------------

	mov AL,DS:COUNTER
	mov DS:T3_prev,AL
	JMP Proc_3_end

; ^^^^^^^^^^^^^^^ End of User main routine ^^^^^^^^^^^^^^^^^^^^^^^^^

SERIAL_REC_ACTION	PROC	FAR
		PUSH	CX
		PUSH 	BX
		PUSH	DS

		MOV	BX,DATA_SEG		;initialize data segment register
		MOV	DS,BX

		CMP	AL,'<'
		JNE	S_FAST

		INC	DS:T_COUNT_SET
		INC	DS:T_COUNT_SET

		JMP	S_NEXT0
S_FAST:
		CMP	AL,'>'
		JNE	S_RET

		DEC	DS:T_COUNT_SET
		DEC	DS:T_COUNT_SET

S_NEXT0:
		MOV	CX,22			;initialize counter for message
		MOV	BX,0

S_NEXT1:	
		MOV	AL,DS:REC_MESS[BX]	;print message
		call	FAR ptr print_char
		INC	BX
		LOOP	S_NEXT1

		MOV	AL,DS:T_COUNT_SET	;print current period of timer0
		CALL	FAR PTR PRINT_2HEX
		;call far ptr print_char
S_RET:
		POP	DS
		POP	BX
		POP	CX
		RET
SERIAL_REC_ACTION	ENDP

TIMER2_ACTION	PROC	FAR
	PUSH	AX
	PUSH	DS
	PUSH	BX
	PUSH	CX

	MOV	AX,DATA_SEG
	MOV	DS,AX

;DEC T1_COUNTER
	MOV AL,DS:T1
	CMP AL,00H
	JE FINISH_ENABLE_T1
	DEC AL
	MOV DS:T1,AL
FINISH_ENABLE_T1:

;DEC T2_COUNTER
	MOV AL,DS:T2
	CMP AL,00H
	JE FINISH_ENABLE_T2
	DEC AL
	MOV DS:T2,AL
FINISH_ENABLE_T2:

;DEC T3_COUNTER
	MOV AL,DS:T3
	CMP AL,00H
	JE FINISH_ENABLE_T3
	DEC AL
	MOV DS:T3,AL
FINISH_ENABLE_T3:

;SYSTEM COUNTER
;	DEC	DS:T_COUNT
;	JNZ	END_TIMER2_ACTION
;	MOV	AL,DS:T_COUNT_SET
;	MOV	DS:T_COUNT,AL
;
	mov AL,DS:COUNTER
	inc AL
	mov DS:COUNTER,AL
;	call far ptr PRINT_2HEX

END_TIMER2_ACTION:	
	POP	CX
	POP	BX
	POP	DS
	POP AX
	RET
TIMER2_ACTION	ENDP

;*************** SUPPORT FUNCTIONS ***************
$include(keypad.inc)
$include(7seg.inc)

KEY_ENQUEUE proc far
	push BX

	mov BX,DS:KEY_QUEUE_TAIL
	MOV DS:KEY_QUEUE_BASE[BX],AL 		;enqueue AL to the tail of queue
	
	INC DS:KEY_QUEUE_TAIL 				;to next element (increase value)
	CMP DS:KEY_QUEUE_TAIL,KEY_QUEUE_LEN ;return once it reaches the end 
	JL END_KEY_ENQUEUE
	MOV DS:KEY_QUEUE_TAIL,0 ;wrap around when reach end of queue

 END_KEY_ENQUEUE:
	pop BX
	ret	
KEY_ENQUEUE endp

PRINT_AL_DEBUG proc far
	push CX
	push BX
	push DS
	push SI

	MOV	BX,DEBUG_SEG	;initialize data segment register
	MOV	DS,BX

	MOV	CX,16 ;length of the debug msg
	MOV	SI,00H ;use SI as a counter

	MOV BX,AX ;store value of AX in BX 

	PRINT_DEBUG_MSG_AL_LOOP:
		mov al,ds:DEBUG_AL[SI]; MOV AL,DS:TIMER0_MESS[SI]
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_DEBUG_MSG_AL_LOOP
	
	mov AX,BX ;restore value of AX
	CALL FAR PTR PRINT_2HEX
	mov AX,BX ;restore value of AX

	pop SI
	pop DS
	pop BX
	pop CX
	RET
PRINT_AL_DEBUG endp

PRINT_T1_PERIOD_DEBUG proc far
	push CX
	push BX
	push DS
	push SI

	MOV	BX,DEBUG_SEG	;initialize data segment register
	MOV	DS,BX

	MOV	CX,13 ;length of the debug msg
	MOV	SI,00H ;use SI as a counter

	MOV BX,AX ;store value of AX in BX 

	PRINT_T1_PERIOD_DEBUG_LOOP:
		mov al,ds:DEBUG_T1[SI]; 
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_T1_PERIOD_DEBUG_LOOP
	
	mov AX,BX ;restore value of AX
	CALL FAR PTR PRINT_2HEX

	pop SI
	pop DS
	pop BX
	pop CX
	RET
PRINT_T1_PERIOD_DEBUG endp

PRINT_T2_PERIOD_DEBUG proc far
	push CX
	push BX
	push DS
	push SI

	MOV	BX,DEBUG_SEG	;initialize data segment register
	MOV	DS,BX

	MOV	CX,13 ;length of the debug msg
	MOV	SI,00H ;use SI as a counter

	MOV BX,AX ;store value of AX in BX 

	PRINT_T2_PERIOD_DEBUG_LOOP:
		mov al,ds:DEBUG_T2[SI]; 
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_T2_PERIOD_DEBUG_LOOP
	
	mov AX,BX ;restore value of AX
	CALL FAR PTR PRINT_2HEX

	pop SI
	pop DS
	pop BX
	pop CX
	RET
PRINT_T2_PERIOD_DEBUG endp

PRINT_PRIORITY_CHECK_MSG_DEBUG proc far
	push CX
	push BX
	push DS
	push SI

	MOV	BX,DEBUG_SEG	;initialize data segment register
	MOV	DS,BX

	MOV	CX,32 ;length of the debug msg
	MOV	SI,00H ;use SI as a counter

	MOV BX,AX ;store value of AX in BX 

	PRINT_PRIORITY_CHECK_MSG_DEBUG_LOOP:
		mov al,ds:DEBUG_PRIORITY[SI]; 
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_PRIORITY_CHECK_MSG_DEBUG_LOOP
	
	mov AX,BX ;restore value of AX

	pop SI
	pop DS
	pop BX
	pop CX
	RET
PRINT_PRIORITY_CHECK_MSG_DEBUG endp

PRINT_DEQUEUE_MSG_DEBUG proc far
	push CX
	push BX
	push DS
	push SI

	MOV	BX,DEBUG_SEG	;initialize data segment register
	MOV	DS,BX

	MOV	CX,11 ;length of the debug msg
	MOV	SI,00H ;use SI as a counter

	MOV BX,AX ;store value of AX in BX 

	PRINT_DEQUEUE_MSG_DEBUG_LOOP:
		mov al,ds:DEBUG_DEQUEUE[SI]; MOV AL,DS:TIMER0_MESS[SI]
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_DEQUEUE_MSG_DEBUG_LOOP
	
	mov AX,BX ;restore value of AX
	CALL FAR PTR PRINT_2HEX
	mov AX,BX ;restore value of AX

	pop SI
	pop DS
	pop BX
	pop CX
	RET
PRINT_DEQUEUE_MSG_DEBUG endp

PRINT_KEYPAD_INPUT_DEBUG proc far
	push CX
	push BX
	push DS
	push SI

	MOV	BX,DEBUG_SEG	;initialize data segment register
	MOV	DS,BX

	MOV	CX,16 ;length of the debug msg
	MOV	SI,00H ;use SI as a counter

	MOV BX,AX ;store value of AX in BX 

	PRINT_KEYPAD_INPUT_DEBUG_LOOP:
		mov al,ds:DEBUG_KEYPAD_INPUT[SI]; MOV AL,DS:TIMER0_MESS[SI]
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_KEYPAD_INPUT_DEBUG_LOOP
	
	mov AX,BX ;restore value of AX
	CALL FAR PTR PRINT_2HEX
	mov AX,BX ;restore value of AX

	pop SI
	pop DS
	pop BX
	pop CX
	RET
PRINT_KEYPAD_INPUT_DEBUG endp

;delay 1s = 16,000,000 clock cycles
fake_process_delay_debug proc far
;	mov CX,0064h ;100 times
	mov CX,10
delay_l1:
	;delay 10ms = 160,000 clock cycles
	mov BX,50	;7272 dec --> total=22*7272=159,984
	delay_l2:	
		nop		; 3 clocks
		dec BX	; 3 clocks
	jnz delay_l2; 16 clocks
	dec CX	;3 clocks cycles
jnz delay_l1 ;16 clocks cycles
	ret
fake_process_delay_debug endp	

;----------------------------------------------------
CODE_SEG	ENDS
END
